package com.t.bricks.bussiness.service.wechat.impl;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.t.bricks.bussiness.db.dao.wechat.AccessTokenDao;
import com.t.bricks.bussiness.model.wechat.SendMsg;
import com.t.bricks.bussiness.service.wechat.WeChatService;
import com.t.bricks.bussiness.util.wechat.TextMessageUtil;
import com.t.bricks.model.Log;
import com.t.bricks.model.MsgEmity;
import com.t.bricks.utils.function.data.IntegerUtil;
import com.t.bricks.utils.function.data.SHA1Util;
import com.t.bricks.utils.function.date.TimeUtil;
import com.t.bricks.utils.function.file.FileUtil;
import com.t.bricks.utils.function.json.JacksonShell;
import com.t.bricks.utils.function.url.HttpsUtil;
import com.t.bricks.utils.spring.ModuleUtil;

/**
 * 微信基本业务操作类接口实现
 */
@Service
public class WeChatServiceImpl implements WeChatService {
		
	/**
	 * 公众号的appid,同时设置在微信平台
	 */
	@Value("${wechat.appid:去微信平台设置}")
	private String appid;
	
	/**
	 * 公众号的appsecret同时设置在微信平台
	 */
	@Value("${wechat.appsecret:去微信平台设置}")
	private String appsecret;
	
	/**
	 * 验证密钥,同时设置在微信平台
	 */
	@Value("${wechat.token:去微信平台设置}")
	private String token;
	
//	/**
//	 * 微信开发者微信号,同时设置在微信平台
//	 */
//	@Value("${wechat.develop.wechatid:去微信平台设置}")
//	private String developWechat;
	
	/**
	 * 微信公众号的登录页所在微信小程序应用程序编号
	 */
	@Value("${wechat.login.view.widget.appid:未设置登录用的微信小程序ip}")
	private String loginViewAppId;
	
	/**
	 * 微信公众号的登录页所在微信小程序应用程序页面地址
	 */
	@Value("${wechat.login.view.widget.url:未设置登录用的微信小程序页面地址}")
	private String loginViewAppUrl;
	
	/**
	 * 消息错误次数阈值(大于多少次则不进行发送)
	 */
	@Value("${app.pusmsg.ierror.total:5}")
	private Integer iErrorTotal;
	
	/**
	 * #微信公众号的程序域名(本公众号域名)
	 */
	@Value("${wechat.official.account.domain:}")
	private String officialAccountDomain;
	
	/**
	 * 手机端页面域名
	 */
	@Value("${app.mobile.server.domain:}")
	private String mobileDomain;
	
	/**
	 * 手机端页面主页
	 */
	@Value("${app.mobile.server.main.url:/app/WechatMonitorManager/index.html/#/main}")
	private String mobileMainUrl;
	
	/**
	 * 手机端页面主页的文字描述
	 */
	@Value("${app.mobile.server.main.text:设备监控}")
	private String mobileMainText;
	
	@Autowired
	private AccessTokenDao accessTokenDao;
	
	/**
	 * 回应处理
	 * @param map 传入的消息
	 * @return
	 */
	@Override
	public String echo(Map<String,String> map) {
		//将微信请求xml转为map格式，获取所需的参数
		String sToUserName = map.get("ToUserName");		//开发者微信号
		String sFromUserName = map.get("FromUserName");	//发送方帐号
		String sMsgType = map.get("MsgType");			//消息类型
		String sContent = map.get("Content");			//消息内容
		String sEvent = map.get("Event");				//事件类型,subscribe(订阅)、unsubscribe(取消订阅)
		String sEventKey = map.get("EventKey");			//事件主键,一般在点击菜单时会有
		
		if (null == sContent || "".equals(sContent.trim())) {
			if (null != sEventKey && sEventKey.indexOf("http://") == 0) {
				return new StringBuilder()
					.append("redirect:")
					.append(sEventKey)
					.append("?openid=")
					.append(sFromUserName)
					.append("&userid=aaa&pass=bbb")
					.toString();
			}
			
			if ("LoginClick".equals(sEventKey)) {
				return new StringBuilder()
					.append("redirect:")
					.append(sEventKey)
					.append("?openid=")
					.append(sFromUserName)
					.append("&userid=aaa&pass=bbb")
					.toString();
			}
			
			return "";
		}
		
		MsgEmity me = ModuleUtil.callServiceMethod("userAndOpenIdService", "findByOpenId", sFromUserName);
		if (!me.isSuccess()) {
			pushLoginMsg(sFromUserName);//推送'要求登录'的信息
			return "success";
		}
		
		String resultMsg = null;
		if("text".equals(sMsgType)) {//处理文本类型
			resultMsg = TextMessageUtil.initMessage(sFromUserName, sToUserName,
					analyseTxt(sContent));
			return resultMsg;
		}
		
		if ("event".equals(sMsgType)) {
			if ("subscribe".equals(sEvent)) {
				resultMsg = TextMessageUtil.initMessage(sFromUserName, sToUserName,
						"欢迎您的到来，来了就别走了");
			} else if ("unsubscribe".equals(sEvent)) {
				resultMsg = TextMessageUtil.initMessage(sFromUserName, sToUserName,
						"走好，不送！");
			} else {
				Log.error("无法处理的消息类型:", sMsgType, ";内容:>>", map.toString());
				resultMsg = TextMessageUtil.initMessage(sFromUserName, sToUserName,
						"这个请求还没实现，找时间再说，有意见去找开发员");
			}
			
			return resultMsg;
		}
		
		Log.error("无法处理的消息类型:", sMsgType, ";内容:>>", map.toString());
		resultMsg = TextMessageUtil.initMessage(sFromUserName, sToUserName,
				"您这问题有待研究，先歇着吧");

		return resultMsg;
	}
	
	/**
	 * 分析文本内容,转入特定处理方法
	 * @param msg
	 * @return
	 */
	private String analyseTxt(String msg) {
		if (null == msg || "".equals(msg.trim())) {
			return "……沉默是金！";
		}
		
		if (msg.contains("当前告警数")) {
			return "当前告警数为0，您可以放心了！";
		}
		
		if (msg.contains("取消告警") || (msg.contains("取消") && msg.contains("告警")) ) {
			return "没有指定的告警需要取消，也可能您要取消的告警已经被别的人处理了！";
		}
		
		if (msg.contains("设备数")) {
			return "还没设置过设备信息呢！";
		}
		
		if ("/?/./!/。/？/！/，/、/".contains(msg.trim())) {
			return "不要浪费大家的时间，我的忍耐是有限度的！";
		}
		
		if ("/在吗/在吗？/在吗?/在/在？/在?/求助".contains(msg.trim())) {
			return "有话直说！";
		}
		
		String text = new StringBuilder()
			.append("虽然我能看懂'").append(msg).append("'这些字,但我还是太笨了，没人教过这个问题怎么回答！\n")
			.append("要不试一下访问这里吧:\n")
			.append("<a href=\"").append(mobileDomain).append(mobileMainUrl).append("\">").append(mobileMainText).append("</a>")
			.toString();
		
		return text;
	}

	/**
	 * 微信端验证链接用接口,已经完整,不用改变
	 * 注意:参数都是腾讯方自动提供,不要尝试自己调用这个接口
	 * @param signature 腾讯自动提供,不要尝试去获取,无意义
	 * @param timestamp 时间戳
	 * @param nonce 随机值
	 * @param echostr 正确时的返回值
	 * @return 正确时返回echostr
	 */
	@Override
	public String check(String signature, String timestamp, String nonce, String echostr) {
		//--根据参数,把在在腾讯平台预定义的TOKEN值和传入的参数进行加密--//
        String[] strArray = {token, timestamp, nonce};//腾讯方要求的排序顺序
        Arrays.sort(strArray);//java自带的排序方法
        StringBuilder sb = new StringBuilder();
        for (String str : strArray) {
            sb.append(str);
        }

		String sKey = SHA1Util.encode(sb.toString());//腾讯方要求的排序顺序,腾讯要求用sha1加密
		
		//--校验,将本地加密结果与传入的signature对照,如果双方加密值一致则通信正确--//
		if (sKey == null || sKey == "" || !sKey.equals(signature)) {
			Log.error("腾讯校验签名失败");
			return "";
		}
		
		Log.debug("腾讯校验签名通过");
		
		return echostr;//如果检验成功原样返回echostr，微信服务器接收到此输出，才会确认检验完成。
	}
	
	/**
	 * 推送'要求登录'的信息
	 * @param toUser
	 * @return
	 */
	private MsgEmity pushLoginMsg(String toUser) {
		MsgEmity me = accessTokenDao.getToken();
		if (!me.isSuccess()) {
			return me;
		}
		
		String accessToken = me.getData();

		String filePath = new StringBuilder()
			.append(System.getProperty("user.dir"))
			.append("/data/rear/MsgTemplate/LoginMsg.json").toString();
		
		me = FileUtil.readFile(filePath);
		if (!me.isSuccess()) {
			return me;
		}
		
		String msg = me.getData();
		if (null == msg || "".equals(msg)) {
			Log.error("读取消息模板文件'LoginMsg.json'失败,请检查是否存在,读取目标:", filePath);
			return MsgEmity.err(8001, "读取消息模板文件'LoginMsg.json'失败,请检查是否存在,读取目标:", filePath);
		}
		
		String url = new StringBuilder("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=")
				.append(accessToken).toString();
		
		msg = msg.replace("${toUser}", toUser)
				.replace("${WidgetAppId}", loginViewAppId)
				.replace("${WidgetAppUrl}", loginViewAppUrl)
				.replace("${toUser}", toUser)
				.replace("${domain}", officialAccountDomain)
				.replace("${NowDate}", LocalDateTime.now().toString());
		
		me = HttpsUtil.post(url, msg);
		if (!me.isSuccess()) {
			return me;
		}
		
		String httpStr = me.getData();
		Map<String, Object> map = JacksonShell.toJavaObject(httpStr, Map.class);
		if (null == map) {
			return MsgEmity.err(8002, "发送消息失败,未能获取返回信息");
		}
		
		int errorCode = (Integer)map.get("errcode");
		if (0 == errorCode) {
			return MsgEmity.success(8999, "发送成功");
		}

		Log.error("发送失败,返回异常信息:", httpStr);
		
		return MsgEmity.err(8003, "发送消息失败,返回信息:", httpStr);
	}
	
	/**
	 * 获取登录页面
	 * @return
	 */
	@Override
	public MsgEmity loginView() {
		String filePath = new StringBuilder().append(System.getProperty("user.dir"))
			.append("/data/front/app/login.html").toString();
		
		MsgEmity me = FileUtil.read(filePath);
		if (!me.isSuccess()) {
			return new MsgEmity(false, "页面内容丢失了,快找程序猿!!", 8001);
		}
		
		return me;
	}
	
	/**
	 * 创建自定义菜单
	 * @return
	 */
	@Override
	public MsgEmity initMenu() {
		MsgEmity me = accessTokenDao.getToken();
		if (!me.isSuccess()) {
			return me;
		}
		
		String accessToken = me.getData();
		
		String filePath = new StringBuilder()
			.append(System.getProperty("user.dir"))
			.append("/data/rear/menu/MainMenu.json").toString();
		
		me = FileUtil.read(filePath);
		if (!me.isSuccess()) {
			return me;
		}
		
		String jsonMenu = me.getData();
		jsonMenu = jsonMenu.replaceAll("\\$\\{mobileDomain\\}", mobileDomain);//美元符号与大括号属于特殊字符
		jsonMenu = jsonMenu.replaceAll("\\$\\{mobileDomain\\}", mobileDomain);//美元符号与大括号属于特殊字符
		jsonMenu = jsonMenu.replace("${officialAccountDomain}", officialAccountDomain);
		
		String url = new StringBuilder("https://api.weixin.qq.com/cgi-bin/menu/create?access_token=")
			.append(accessToken).toString();
				
		// 发起POST请求创建菜单
		me = HttpsUtil.post(url, jsonMenu);
		if (!me.isSuccess()) {
			return me;
		}
		
		String httpStr = me.getData();
		Map<String, Object> map = JacksonShell.toJavaObject(httpStr, Map.class);
		if (null == map) {
			return MsgEmity.err(8002, "初始化菜单失败,未能获取返回信息");
		}

		int errorCode = IntegerUtil.objToInt(map.get("errcode"), -1);
		if (-1 == errorCode) {
			return MsgEmity.err(8002, "初始化菜单失败,未返回预定信息");
		}
		
		if (0 != errorCode) {
			return MsgEmity.err(8003, "初始化菜单失败,返回信息:", httpStr);
		}

		return MsgEmity.success("初始化成功");
	}

	/**
	 * 推送消息
	 * @param sendMsg 消息对象
	 * @param isNewMsg 是否新消息
	 * @return
	 */
	@Override
	public MsgEmity pushMsg(SendMsg sendMsg, boolean isNewMsg) {
		if (null == sendMsg.getsRecipientsId() || "".equals(sendMsg.getsRecipientsId().trim())) {
			return MsgEmity.err(8021, "接收人编号不能为空");
		}
		
		if (null == sendMsg.getsSpokesman() || "".equals(sendMsg.getsSpokesman().trim())) {
			return MsgEmity.err(8022, "发送人不能为空");
		}
		
		if (null == sendMsg.getsClassName() || "".equals(sendMsg.getsClassName().trim())) {
			sendMsg.setsClassName("PushMsg");//设置默认的消息发送
		}

		if (isNewMsg) {
			MsgEmity me = ModuleUtil.callServiceMethod(
					"userAndOpenIdService", "getValueByFieldName",
					new Class<?>[] {String.class, List.class},
					new Object[] { sendMsg.getsRecipientsId(), Arrays.asList("sOpenId") });
			if (null == me || !me.isSuccess()) {
				return MsgEmity.err(8023, "无法获取接收人关注号");
			}
			
			Map<String, String> map = me.getData();
			
			sendMsg.setsRecipientsName(map.get("sOpenId"));//补充
			sendMsg.setiState(2);//状态(枚举,1:待发;2:发送中;3:已发;4:发送成功;5:发送失败过多),因为下一步就要进行发送, 直接占用,省得修改状态
			sendMsg.setiIndex(1);
			sendMsg.setiVersion(1);
			
			String json = JacksonShell.toJSONString(sendMsg);
			me = ModuleUtil.callServiceMethod("sendMsgService", "add",
					new Class<?>[] {Object.class},
					new Object[] { json });//添加到记录
			if (me == null || !me.isSuccess()) {
				return me;
			}
		}
		
		//--部分直接从数据库中提交的数据可能存在关注号缺失的情况,为了避免出现数据异常,在此进行弥补--//
		if (null == sendMsg.getsRecipientsName() || "".equals(sendMsg.getsRecipientsName())) {
			MsgEmity me = ModuleUtil.callServiceMethod(
					"userAndOpenIdService", "getValueByFieldName",
					new Class<?>[] {String.class, List.class},
					new Object[] { sendMsg.getsRecipientsId(), Arrays.asList("sOpenId") });
			if (null == me || !me.isSuccess()) {
				return MsgEmity.err(8023, "无法获取接收人关注号");
			}
			
			Map<String, String> map = me.getData();
			
			sendMsg.setsRecipientsName(map.get("sOpenId"));//补充
		}
		
		MsgEmity me = accessTokenDao.getToken();
		if (!me.isSuccess()) {
			return me;
		}
		
		String accessToken = me.getData();

		String filePath = new StringBuilder()
			.append(System.getProperty("user.dir"))
			.append("/data/rear/MsgTemplate/")
			.append(sendMsg.getsClassName().trim())
			.append(".json").toString();
		
		me = FileUtil.readFile(filePath);
		if (!me.isSuccess()) {
			return me;
		}
		
		String msg = me.getData();
		if (null == msg || "".equals(msg)) {
			Log.error("读取消息模板文件'", sendMsg.getsClassName().trim(), "'失败,请检查是否存在,读取目标:", filePath);
			return MsgEmity.err(8024, "读取消息模板文件'", sendMsg.getsClassName().trim(), "'失败,请检查是否存在,读取目标:", filePath);
		}
		
		String url = new StringBuilder("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=")
				.append(accessToken).toString();
		
		msg = msg.replace("${toUser}", sendMsg.getsRecipientsName())
				.replace("${WidgetAppId}", loginViewAppId)
				.replace("${WidgetAppUrl}", loginViewAppUrl)
				.replace("${jumpUrl}", sendMsg.getsUrl() == null ? "" : sendMsg.getsUrl())
				.replace("${mobileDomain}", mobileDomain)
				.replace("${officialAccountDomain}", officialAccountDomain)
				.replace("${NowDate}", TimeUtil.toStr(sendMsg.getdTime()));
		
		msg = replaceTempletVariable(msg, sendMsg);//用消息对象树形替换模板中的变量,

		if (!isNewMsg) {//如果不是新增的信息,则必须修改状态值
			me = ModuleUtil.callServiceMethod("SendMsgService", "changeState",
					new Class<?>[]{ String.class, Integer.class, Integer.class, String.class },
					new Object[]{ sendMsg.getsId(), 2, sendMsg.getiVersion(), null});//修改状态 //状态(枚举,1:待发;2:发送中;3:已发;4:发送成功;5:发送失败过多)
			if (!me.isSuccess()) {
				Log.error("修改状态值失败,不允许进行下一步(发送)");
				return me;
			}
			
			sendMsg.setiVersion(sendMsg.getiVersion() +1);//改变版本号
		}
		
		me = HttpsUtil.post(url, msg);
		if (!me.isSuccess()) {
			return me;
		}
		
		String httpStr = me.getData();
		Map<String, Object> map = JacksonShell.toJavaObject(httpStr, Map.class);
		if (null == map) {
			ModuleUtil.callServiceMethod("sendMsgService", "addError", sendMsg.getsId(), iErrorTotal, httpStr);//累加错误次数
			return MsgEmity.err(8025, "发送消息失败,未能获取返回信息");
		}
		
		int errorCode = (Integer)map.get("errcode");
		if (0 == errorCode) {
			me = ModuleUtil.callServiceMethod("sendMsgService", "changeState",
					new Class<?>[]{ String.class, Integer.class, Integer.class, String.class },
					new Object[]{ sendMsg.getsId(), 4, sendMsg.getiVersion(), null});//修改状态 //状态(枚举,1:待发;2:发送中;3:已发;4:发送成功;5:发送失败过多)
			
			Log.info("发送消息成功,执行返回信息:", httpStr);
			
			return MsgEmity.success(8999, "发送成功");
		} else if (40003 == errorCode) {//接收者账号不存在
			ModuleUtil.callServiceMethod("sendMsgService", "addError", sendMsg.getsId(), iErrorTotal, httpStr);//累加错误次数
			return MsgEmity.err(8026, "发送消息失败,接收者账号可能不存在,返回信息:", httpStr);
		}

		Log.error("发送失败,返回异常信息:", httpStr);
		ModuleUtil.callServiceMethod("sendMsgService", "addError", sendMsg.getsId(), iErrorTotal, httpStr);//累加错误次数
		
		return MsgEmity.err(8027, "发送消息失败,将在后续继续尝试发送,返回信息:", httpStr);
	}
	
	/**
	 * 用消息对象树形替换模板中的变量
	 * @param msg
	 * @param sendMsg
	 * @return
	 */
	private String replaceTempletVariable(String msg, SendMsg sendMsg) {
		Map<String, Object> map = JacksonShell.toJavaObject(sendMsg, Map.class);
		if (null == map) {//转换失败
			String json = JacksonShell.toJSONString(sendMsg);
			Log.error(json);
			return msg;
		}
		
		StringBuilder sb = new StringBuilder();
		
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			sb.setLength(0);
			sb.append("\\$\\{").append(entry.getKey()).append("\\}");
			msg = msg.replaceAll(sb.toString(), (entry.getValue() == null || "null".equals(entry.getValue())) ? "" : entry.getValue().toString());
		}
		
		if (null == sendMsg.getsMsgSub() || "".equals(sendMsg.getsMsgSub().trim())) {
			return msg;
		}
		
		map = JacksonShell.toJavaObject(sendMsg.getsMsgSub(), Map.class);
		if (null == map) {
			return msg;
		}
		
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			sb.setLength(0);
			sb.append("\\$\\{").append(entry.getKey()).append("\\}");
			msg = msg.replaceAll(sb.toString(), (entry.getValue() == null || "null".equals(entry.getValue())) ? "" : entry.getValue().toString());
		}
		
		return msg;
	}
	
	/**
	 * 微信重定向
	 * @param code 来自腾讯的请求码
	 * @param page 跳转的页面
	 * @return
	 */
	@Override
	public MsgEmity redirect(String code, String page) {
		String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"
        		.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code);
		MsgEmity me = HttpsUtil.get(URL, null);
		if (!me.isSuccess()) {
			return MsgEmity.err(8001, "请求发生错误");
		}

		Map<String, Object> map = JacksonShell.toJavaObject(me.getData(), Map.class);
		if (null == map) {
			return MsgEmity.err(8002, "未获得相应结果,可能腾讯又改接口了,请联系管理员");
		}
		
		//page = page.replaceAll("/#/", "/%23/");
		
		return new MsgEmity(true, "redirect:STATE".replace("STATE", page), map.get("openid").toString());
	}
	
}
